﻿#include <linux/module.h>
#include <asm/desc.h>

#include "exceptions.h"
#include "paging.h"
#include "paging_api.h"
#include "testing.h"

#define info(...) printk("<1> info " __VA_ARGS__)

#define PFEC(x) ( ((x) & 1) + \
                  (((x)>>1) & 1)*10 + \
                  (((x)>>2) & 1)*100 + \
                  (((x)>>3) & 1)*1000 + \
                  (((x)>>4) & 1)*10000 )
                  
#pragma GCC push_options
#pragma GCC optimize ("O0")

void int_info_header(void)
{
  info("%-10s%6s%9s%11s%11s",
       "test",
       "int?",
       "int num",
       "err code?",
       "err_code"
      );
}

void int_info(char *name)
{
  info("%-10s%6s%9hu%11s%11d",
       name,
       (last_int_info.interrupt ? "yes" : "no"),
       last_int_info.int_num,
       (last_int_info.has_error_code ? "yes" : "no"),
       PFEC(last_int_info.unused2)
      );
}

struct paging_vertex vertices[13];
struct paging_entry entries[13];
struct paging pg;


char *case_comments[] = {
  "{ 0 -[W]-> 1 -[W]-> 2 -[W]-> 3 -[W]-> [4] }",
  "{ 0 -[W]-> 1 -[W]-> 2 -[X][W]-> 5 }",
  "{ 0 -[W]-> 1 -[W]-> 2 -[X]> 6 }",
  "{ 0 -[W]-> 1 -[W]-> 2 -> 7 }",
  "{ 0 -> 8 -> 9 -[W]-> 10 }",
  "{ 0 -[W]-> 11 -[W]-> 12 -[W]-> 10 }",
  "not present address"
};

char *case_act[] = {
  "read",
  "write",
  "run"
};

/*
 * init simple paging
 * 
 * 0 -[W]-> 1 -[W]-> 2 -[W]->    3 -[W]-> [4]
 *                     -[X][W]-> 5
 *                     -[X]>     6
 *                     ->        7
 * 
 *   ->     8      -> 9  -[W]-> 10
 *   -[W]-> 11 -[W]-> 12 -[W]-> 10
 * 
 * let's hardcode it ;)
 */

#define PFPI (16 | 1)
#define PFPW (2 | 1)
#define PFI (16)
#define PFW (2)

struct interrupt_info answer[] = {
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 111, interrupt: 1, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
  
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFPI },
  
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFPW },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFPI },
  
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFPW },
{ int_num: 111, interrupt: 1, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
  
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFPW },
{ int_num: 111, interrupt: 1, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
  
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 0, interrupt: 0, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
{ int_num: 111, interrupt: 1, has_error_code: 0,unused0:0,unused1:0, unused2: 0 },
  
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: 0 },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFW },
{ int_num: 14, interrupt: 1, has_error_code: 1,unused0:0,unused1:0, unused2: PFI }
};


/*
 * init simple paging
 * 
 * 0 -[W]-> 1 -[W]-> 2 -[W]->    3 -[W]-> [4]
 *                     -[X][W]-> 5
 *                     -[X]>     6
 *                     ->        7
 * 
 *   ->     8      -> 9  -[W]-> 10
 *   -[W]-> 11 -[W]-> 12 -[W]-> 10
 * 
 * let's hardcode it ;)
 */

static void my_init_paging(void)
{
  int i;
  int pages[] = { 4, 5, 6, 7, 10 };
  int tables[] = { 0,1,2,3,8,9,11,12 };
  pg.paging_mode = PM_64;
  pg.root = vertices;
  pg.vertices = vertices;
  pg.n_vertices = 13;
  
  vertices[4].offset_mask = OFFSET_MASK_4KB;
  vertices[5].offset_mask = OFFSET_MASK_2MB;
  vertices[6].offset_mask = OFFSET_MASK_2MB;
  vertices[7].offset_mask = OFFSET_MASK_2MB;
  vertices[10].offset_mask = OFFSET_MASK_2MB;
  
  
  vertices[0].entries = &entries[0];
  vertices[0].n_entries = 3;
    vertices[0].entries[0].child = &vertices[1]; 
    vertices[0].entries[0].flags = PF_PRESENT | PF_WRITE;
    vertices[0].entries[1].child = &vertices[8]; 
    vertices[0].entries[1].flags = PF_PRESENT;
    vertices[0].entries[2].child = &vertices[11]; 
    vertices[0].entries[2].flags = PF_PRESENT | PF_WRITE;
  vertices[1].entries = &entries[3];
  vertices[1].n_entries = 1;
    vertices[1].entries[0].child = &vertices[2]; 
    vertices[1].entries[0].flags = PF_PRESENT | PF_WRITE;
  vertices[2].entries = &entries[4];
  vertices[2].n_entries = 4;
    vertices[2].entries[0].child = &vertices[3]; 
    vertices[2].entries[0].flags = PF_PRESENT | PF_WRITE | 0;
    vertices[2].entries[1].child = &vertices[5]; 
    vertices[2].entries[1].flags = PF_PRESENT | PF_WRITE | PF_UNEXECUTABLE |  PF_MAP_PAGE;
    vertices[2].entries[1].pos = 1;
    vertices[2].entries[2].child = &vertices[6]; 
    vertices[2].entries[2].flags = PF_PRESENT | PF_UNEXECUTABLE | PF_MAP_PAGE;
    vertices[2].entries[2].pos = 2;
    vertices[2].entries[3].child = &vertices[7]; 
    vertices[2].entries[3].flags = PF_PRESENT | PF_MAP_PAGE;
    vertices[2].entries[3].pos = 3;
  vertices[3].entries = &entries[8];
  vertices[3].n_entries = 1;
    vertices[3].entries[0].child = &vertices[4]; 
    vertices[3].entries[0].flags = PF_PRESENT | PF_WRITE;
  vertices[8].entries = &entries[9];
  vertices[8].n_entries = 1;
    vertices[8].entries[0].child = &vertices[9]; 
    vertices[8].entries[0].flags = PF_PRESENT;
  vertices[9].entries = &entries[10];
  vertices[9].n_entries = 1;
    vertices[9].entries[0].child = &vertices[10]; 
    vertices[9].entries[0].flags = PF_PRESENT | PF_WRITE | PF_MAP_PAGE;
  vertices[11].entries = &entries[11];
  vertices[11].n_entries = 1;
    vertices[11].entries[0].child = &vertices[12]; 
    vertices[11].entries[0].flags = PF_PRESENT | PF_WRITE;
  vertices[12].entries = &entries[12];
  vertices[12].n_entries = 1;
    vertices[12].entries[0].child = &vertices[10]; 
    vertices[12].entries[0].flags = PF_PRESENT | PF_WRITE | PF_MAP_PAGE;
  
  
  for (i=0; i<5; ++i) {
    vertices[pages[i]].entries = 0;
  }
  for (i=0; i<8; ++i) {
    vertices[tables[i]].in_memory = 1;
  }
}


struct test_case cases[7*3];
struct test t;



/*
 * copied comment
 * 
 * 0 -[W]-> 1 -[W]-> 2 -[W]->    3 -[W]-> [4]
 *                     -[X][W]-> 5
 *                     -[X]>     6
 *                     ->        7
 * 
 *   ->     8      -> 9  -[W]-> 10
 *   -[W]-> 11 -[W]-> 12 -[W]-> 10
 * 
 * let's hardcode it ;)
 */

static void my_init_test(void)
{
  int i;
  int page_ind[] = {4,5,6,7,10,10,-1};
  vaddr_t arr[7];
  
  arr[0] = vaddr64_5(0, vertices[1].entries[0].pos,
                        vertices[2].entries[0].pos,
                        vertices[3].entries[0].pos,
                        0);
  
  arr[1] = vaddr64_4(0, vertices[1].entries[0].pos,
                        vertices[2].entries[1].pos,
                        17);
  
  arr[2] = vaddr64_4(0, vertices[1].entries[0].pos,
                        vertices[2].entries[2].pos,
                        17);
  
  arr[3] = vaddr64_4(0, vertices[1].entries[0].pos,
                        vertices[2].entries[3].pos,
                        17);
  
  arr[4] = vaddr64_4(1, vertices[8].entries[0].pos,
                        vertices[9].entries[0].pos,
                        2*1024*1024 - 100);
  
  arr[5] = vaddr64_4(2, vertices[11].entries[0].pos,
                        vertices[12].entries[0].pos,
                        2*1024*1024 - 100);
  
  arr[6] = vaddr64_4(1, vertices[8].entries[0].pos+1,
                        vertices[9].entries[0].pos,
                        2*1024*1024 - 100);
  
  for (i = 0; i < 7; ++i) {
    cases[i*3+0].id = i*3+0;
    cases[i*3+0].action = ACTION_READ;
    cases[i*3+0].vaddr = arr[i];
    cases[i*3+0].vertex = vertices + page_ind[i];

    cases[i*3+1].id = i*3+1;
    cases[i*3+1].action = ACTION_WRITE;
    cases[i*3+1].vaddr = arr[i];
    cases[i*3+1].vertex = vertices + page_ind[i];

    cases[i*3+2].id = i*3+2;
    cases[i*3+2].action = ACTION_RUN;
    cases[i*3+2].vaddr = arr[i];
    cases[i*3+2].vertex = vertices + page_ind[i];
  }
  
  
  cases[6*3+0].vertex = 0;
  cases[6*3+1].vertex = 0;
  cases[6*3+2].vertex = 0;
  
  cases[14].action = ACTION_RUN;
  
  t.cases = cases;
  t.n_cases = 3*7;
  t.pg = &pg;
}

int OK;
void send_to_host(const void *buf, int count)
{
  struct test_case_result *res;
  int ok;
  if (count != sizeof(struct test_case_result)) {
    info("send_to_host error: count = %d", count);
    return;
  }
  
  res = (struct test_case_result *) buf;
  ok = *((u64*)(res)) == *((u64*)(answer + res->id));
  if(ok) OK++;
  
  if (res->int_info.interrupt) {
    if (res->int_info.int_num == 14) {
      printk("\n#%d: %s %s\nvaddr: %p\n  page fault: %05d [IRUWP]  :  %s",
             res->id,
             case_act[res->id % 3],
             case_comments[res->id / 3],
             cases[res->id].vaddr,
             PFEC(res->int_info.unused2),
             (ok ? "OK" : "FAIL"));
    }
    else {
      printk("\n#%d: %s %s\nvaddr: %p\n  interrupt %hu  :  %s",
             res->id,
             case_act[res->id % 3],
             case_comments[res->id / 3],
             cases[res->id].vaddr,
             res->int_info.int_num,
             (ok ? "OK" : "FAIL"));
    }
  }
  else {
    printk("\n#%d: %s %s\nvaddr: %p\n no interrupts  :  %s",
            res->id,
             case_act[res->id % 3],
             case_comments[res->id / 3],
             cases[res->id].vaddr,
             (ok ? "OK" : "FAIL"));
  }
}


static int my_init(void)
{
    info("begin");

    init_exception_handling();
    
    my_init_paging();  
    init_paging(&pg);
    
    my_init_test();
    init_test(&t);
    
    
    
    run_test(&t);
    
    info("OK:%d\nTOTAL:%d\nfinish", OK, t.n_cases);
    
    return 0;
}

static void my_exit(void) {
    info("exit");
}

#pragma GCC pop_options

module_init(my_init);
module_exit(my_exit);
